package owg.util.data;

import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import owg.util.Calc;

abstract class PFieldAbstract<T extends PFieldElement> implements PField<T>
{	
	/**Typed listeners*/
	protected ArrayList<WeakReference<PFieldListener<? super T>>> childListeners = new ArrayList<>();
	
	protected PFieldOwner<? super T> owner;
	/**The type of the property*/
	protected Class<T> type;
	/**Whether there are any listeners that have not been notified of a new change.*/
	protected boolean dirty = false;
	/**All registered annotations on this instance.*/
	protected HashMap<Class<?>, Object> favorites = new HashMap<>();
	
	/**Assigned by {@link PFieldUtil}*/
	boolean discovered = false;
	/**Assigned by {@link PFieldUtil}*/
	Object discoveredOwner = null;
	/**Assigned by {@link PFieldUtil}*/
	Field discoveredField = null;
	
	public PFieldAbstract(PFieldOwner<? super T> owner, Class<T> childType)
	{
		this.owner = owner;
		this.type = childType;
	}
	
	@Override
	public PFieldOwner<? super T> getOwner()
	{
		return owner;
	}
	@Override
	public Class<T> getType()
	{
		return type;
	}
	
	@Override
	public void addListener(Referencer parent, PFieldListener<? super T> l)
	{
		if(parent != null)
			parent.addReference(l);
		if(l == null)
		{
			System.err.println("Cannot add null as listener to "+this);
			return;
		}
		int oldIndex = Calc.indexOfWeakReference(childListeners, l);
		if(oldIndex != -1)
		{
			System.err.println("Warning: Tried to add "+l+" as listener to "+this+" but it was already added at "+oldIndex);
			return;
		}
		childListeners.add(new WeakReference<PFieldListener<? super T>>(l));
	}
	
	@Override
	public boolean removeListener(Referencer parent, PFieldListener<? super T> l)
	{
		if(parent != null)
			parent.removeReference(l);
		int index = Calc.indexOfWeakReference(childListeners, l);
		if(index == -1)
			return false;
		childListeners.remove(index);
		return true;
	}
	
	protected void checkVetos(Object exclude, T newValue, int index)
	{
		if(exclude != owner && owner != null)
			owner.pFieldChange(this, newValue, index);
	}
	
	protected void fireEvents(int index, T oldValue, T newValue, Object exclude)
	{
		dirty = true;
		
		Iterator<WeakReference<PFieldListener<? super T>>> it = childListeners.iterator();
		int iterationIndex = 0;
		while(it.hasNext())
		{
			PFieldListener<? super T> next;
			try
			{
				next = it.next().get();
			}
			catch(Exception e)
			{
				String lString = toString(childListeners);
				throw new RuntimeException("Iteration failure at "+iterationIndex+":\n"+
					"Listeners = "+lString
					, e);
			}
			iterationIndex++;
			
			if(next != null)
			{
				if(next != exclude)
				{
					next.valueChanged(this, index, oldValue, newValue);
				}
				if(!dirty)
					return;
			}
			else
				it.remove();//Clean up destroyed reference
		}
		dirty = false;
	}
	protected String toString(ArrayList<? extends WeakReference<?>> vetoListeners2)
	{
		StringBuilder sb = new StringBuilder();
		int idx = 0;
		for(WeakReference<?> l : vetoListeners2)
		{
			Object e = l.get();
			if(idx != 0)
				sb.append(", ");
			if(e == null)
				sb.append("null");
			else
				sb.append(e.toString());
			idx++;
		}
		return sb.toString();
	}
	
	@Override
	public void registerFavorite(Object a)
	{
		favorites.put(a.getClass(), a);
	}
	@Override
	public void removeFavorite(Class<?> a)
	{
		favorites.remove(a);
	}
	@Override
	public<X> X getFavorite(Class<X> type)
	{
		@SuppressWarnings("unchecked")
		X r = (X) favorites.get(type);
		return r;
	}
}
